/*
 * User: rant
 * Date: 21/12/2006
 *
 *
 */
package laMalinche.rete.nodes;

import laMalinche.DerivationAdviser;
import laMalinche.rete.nodes.tailing.WorkingMemoryChangeListener;
import laMalinche.rete.nodes.tailing.WorkingMemoryEvent;
import laMalinche.rete.rule.Action;
import laMalinche.rete.rule.ActionType;

import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import java.util.*;

/**
 *
 */
public class ActivatorNode<T extends Action> extends BasicReteNode implements ReteNode, WorkingMemoryChangeListener {
	private ReteNode node;
	private T ruleActivator;
	private boolean stopEvaluatingMemoryChangeFlag;

	public ActivatorNode() {
	}

	public boolean isActive() {
		return (isConditionMet() != null && isConditionMet());
	}

	public void updateActivatorConditions() {
		// false may be changed
		if (!FALSE.equals(isConditionMet())) {
			List<ConditionNode> conditions = getConditions();
			for (ConditionNode conditionNode : conditions) {
				conditionNode.update();
				if (!TRUE.equals(conditionNode.isConditionMet())) {
					return;
				}
			}
		}
	}

	private List<ConditionNode> getConditions() {
		// todo implement getConditions in nodes
		ReteNode reteNode = node;
		Stack<ReteNode> stack = new Stack<ReteNode>();
		stack.push(reteNode);
        List<ConditionNode> conditions = new ArrayList<ConditionNode>();
		while (!stack.isEmpty()) {
			if (reteNode instanceof MergerNode) {
				MergerNode mergerNode = (MergerNode) stack.pop();
                ReteNode leftNode = mergerNode.getLeftNode();
                if (leftNode instanceof MergerNode) {
					stack.push(leftNode);
				} else {
					conditions.add((ConditionNode) leftNode);
				}
                ReteNode rightNode = mergerNode.getRightNode();
                if (rightNode instanceof MergerNode) {
					stack.push(rightNode);
				} else {
					conditions.add((ConditionNode) rightNode);
				}
			} else {
				conditions.add((ConditionNode) stack.pop());
			}
		}
		return conditions;
	}

	//todo
	private List<ConditionNode> sortConditionsByType(List<ConditionNode> conditions) {
		return null;
	}

	public ReteNode getNode() {
		return node;
	}

	public ActivatorNode(T ruleActivator) {
		this.ruleActivator = ruleActivator;
	}

	public T getRuleActivator() {
		return ruleActivator;
	}

	public void activate() {
		activate(false);
	}

	private void activate(boolean force) {
		if (isConditionMet() != null && isConditionMet()) {
			T action = getRuleActivator();
			stopEvaluatingMemoryChangeFlag = true;
			action.execute();
			stopEvaluatingMemoryChangeFlag = false;
		}
	}

	public void setRuleActivator(T ruleActivator) {
		this.ruleActivator = ruleActivator;
	}

	public void workingMemoryChange(WorkingMemoryEvent event) {
		if (!stopEvaluatingMemoryChangeFlag) {
			final Boolean met = event.isConditionMet();
			final T action = getRuleActivator();
			if (action instanceof DerivationAdviser) {
				((DerivationAdviser) action).informRuleUpdated(met);
			}
			if (TRUE.equals(met)) {
				activate();
			}
		}
	}

	@Override
	public Boolean isConditionMet() {
		return node.isConditionMet();
	}

	public void addMerger(ReteNode mergerNode) {
		if (node != null) {
			throw new RuntimeException("only one merger is accepted");
		}
		((BasicRelevantListener) mergerNode).addWorkingMemoryListener(this);
		node = mergerNode;
	}

	public ActionType type() {
		return getRuleActivator().getActionType();
	}
}